from fastapi import FastAPI, File, UploadFile, HTTPException
from fastapi.responses import JSONResponse
import os
import tempfile
import shutil
from typing import Dict, Any, List, Optional
import easyocr
from openai import OpenAI, APIError
from PyPDF2 import PdfReader
from docx import Document
import numpy as np
import cv2
import re
from datetime import datetime

# 创建FastAPI应用
app = FastAPI(title="考勤记录分析API", description="分析考勤记录文件并返回标准化JSON响应")

# API密钥配置
API_KEY = "sk-20856422ed6644e3827b9d5403c9542a"  # 替换为你的API密钥


def classify_attendance_type(text: str) -> str:
    """根据文本内容识别考勤记录类型"""
    categories = {
        # 按记录形式
        "打卡记录": ["打卡", "刷卡", "指纹", "人脸识别", "上班打卡", "下班打卡", "考勤机"],
        "签到表": ["签到", "签退", "到岗", "离岗", "出勤表", "签名"],
        "工时统计表": ["工时", "工作时长", "累计时间", "总工时", "有效工时"],
        "加班记录": ["加班", "延时", "超时", "加班费", "加班申请", "加班时长"],
        "请假记录": ["请假", "病假", "事假", "年假", "婚假", "产假", "丧假", "调休"],
        "出差记录": ["出差", "外勤", "外出", "差旅", "出差申请"],
        "值班记录": ["值班", "夜班", "轮班", "倒班", "班次"],
        "迟到早退记录": ["迟到", "早退", "缺勤", "旷工", "异常"],
        
        # 按统计周期
        "日考勤记录": ["日期", "当日", "今日", "本日"],
        "周考勤汇总": ["本周", "周汇总", "一周", "星期"],
        "月考勤汇总": ["本月", "月汇总", "月度", "当月"],
        "年度考勤统计": ["年度", "全年", "年终", "年汇总"],
        
        # 按管理用途
        "薪资计算依据": ["薪资", "工资", "计薪", "绩效", "奖金"],
        "绩效考核记录": ["绩效", "考核", "评估", "KPI", "目标完成"],
        "违规处罚记录": ["违规", "处罚", "警告", "扣分", "违纪"],
        "调岗调班记录": ["调岗", "调班", "换班", "岗位调整"]
    }

    for name, keywords in categories.items():
        if any(keyword in text for keyword in keywords):
            return name
    return "考勤单"


def extract_company_name(text: str) -> str:
    """从文本中提取公司名称"""
    company_patterns = [
        r"([\u4e00-\u9fa5]+(?:有限公司|股份有限公司|集团|公司|企业|厂))",
        r"公司[：:：]\s*([\u4e00-\u9fa5]+(?:有限公司|股份有限公司|集团|公司|企业|厂))",
        r"单位[：:：]\s*([\u4e00-\u9fa5]+(?:有限公司|股份有限公司|集团|公司|企业|厂))",
        r"([\u4e00-\u9fa5]{2,10}(?:有限公司|股份有限公司|集团|公司))"
    ]
    
    for pattern in company_patterns:
        matches = re.findall(pattern, text)
        if matches:
            # 返回最长的匹配结果，通常更准确
            return max(matches, key=len)
    
    return ""


def extract_date_range(text: str) -> tuple:
    """从文本中提取起始和结束日期"""
    # 匹配各种日期格式
    date_patterns = [
        r"(\d{4})年(\d{1,2})月",
        r"(\d{4})-(\d{1,2})",
        r"(\d{4})\.(\d{1,2})",
        r"(\d{4})/(\d{1,2})"
    ]
    
    dates = []
    for pattern in date_patterns:
        matches = re.findall(pattern, text)
        for match in matches:
            year, month = match
            dates.append(f"{year}年{int(month):02d}月")
    
    # 去重并排序
    dates = sorted(list(set(dates)))
    
    if not dates:
        return "", ""
    elif len(dates) == 1:
        return dates[0], dates[0]
    else:
        return dates[0], dates[-1]


def analyze_file_validity(text: str, attendance_type: str) -> tuple:
    """分析文件有效性并判断是否可作为证据"""
    validity_issues = []
    can_be_evidence = True
    
    # 检查基本信息完整性
    if not extract_company_name(text):
        validity_issues.append("缺少公司名称信息")
    
    start_date, end_date = extract_date_range(text)
    if not start_date:
        validity_issues.append("缺少日期信息")
    
    # 检查考勤记录的关键要素
    key_elements = ["姓名", "工号", "时间", "签到", "签退", "打卡", "考勤"]
    found_elements = sum(1 for element in key_elements if element in text)
    
    if found_elements < 2:
        validity_issues.append("考勤记录关键要素不足")
        can_be_evidence = False
    
    # 检查文本长度和内容质量
    if len(text.strip()) < 50:
        validity_issues.append("文件内容过少，可能不完整")
        can_be_evidence = False
    
    # 检查是否包含明显的考勤相关内容
    attendance_keywords = ["考勤", "打卡", "签到", "上班", "下班", "工时", "出勤"]
    if not any(keyword in text for keyword in attendance_keywords):
        validity_issues.append("文件内容与考勤记录不符")
        can_be_evidence = False
    
    if not validity_issues:
        validity_description = "文件格式规范，内容完整，符合考勤记录要求"
    else:
        validity_description = "存在以下问题：" + "；".join(validity_issues)
    
    return validity_description, "是" if can_be_evidence else "否"


class AttendanceAnalyzer:
    def __init__(self, api_key: str):
        self.client = OpenAI(
            api_key=api_key,
            base_url="https://api.deepseek.com"
        )

    def _extract_from_image(self, file_path: str, reader=None) -> str:
        try:
            if not os.path.exists(file_path):
                raise FileNotFoundError(f"文件不存在: {file_path}")
            img_array = np.fromfile(file_path, dtype=np.uint8)
            image = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
            if image is None:
                raise ValueError("无法解析图像，cv2.imdecode 失败")
            reader = reader or easyocr.Reader(['ch_sim', 'en'])
            result = reader.readtext(image, detail=0)
            return "\n".join(result).strip()
        except Exception as e:
            raise RuntimeError(f"图片OCR识别失败: {str(e)}")

    def extract_text(self, file_path: str) -> str:
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"文件不存在: {file_path}")
        ext = file_path.lower()
        if ext.endswith('.pdf'):
            return self._extract_from_pdf(file_path)
        elif ext.endswith('.docx'):
            return self._extract_from_docx(file_path)
        elif ext.endswith(('.png', '.jpg', '.jpeg', '.bmp', '.tiff', '.webp')):
            return self._extract_from_image(file_path)
        else:
            raise ValueError("不支持的文件格式，请提供PDF、DOCX或图片文件")

    def _extract_from_pdf(self, file_path: str) -> str:
        text = ""
        try:
            with open(file_path, 'rb') as file:
                reader = PdfReader(file)
                for page in reader.pages:
                    text += page.extract_text() or ""
        except Exception as e:
            raise RuntimeError(f"PDF文件读取失败: {str(e)}")
        return text

    def _extract_from_docx(self, file_path: str) -> str:
        try:
            doc = Document(file_path)
            return "\n".join([para.text for para in doc.paragraphs if para.text])
        except Exception as e:
            raise RuntimeError(f"DOCX文件读取失败: {str(e)}")


def validate_file_type(filename: str) -> bool:
    """验证文件类型是否支持"""
    allowed_extensions = {'.pdf', '.docx', '.png', '.jpg', '.jpeg', '.bmp', '.tiff', '.webp'}
    file_ext = os.path.splitext(filename.lower())[1]
    return file_ext in allowed_extensions


@app.post("/analyze_attendance")
async def analyze_attendance(attendance_file: UploadFile = File(...)):
    """
    分析考勤记录文件
    
    Args:
        attendance_file: 上传的考勤文件（支持PDF、DOCX及图片格式）
    
    Returns:
        JSON格式的分析结果
    """
    
    # 验证文件类型
    if not validate_file_type(attendance_file.filename):
        raise HTTPException(
            status_code=400,
            detail="不支持的文件格式，请上传PDF、DOCX或图片文件"
        )
    
    # 创建临时文件
    temp_file = None
    try:
        # 保存上传的文件到临时目录
        with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(attendance_file.filename)[1]) as temp_file:
            shutil.copyfileobj(attendance_file.file, temp_file)
            temp_file_path = temp_file.name
        
        # 初始化分析器
        analyzer = AttendanceAnalyzer(API_KEY)
        
        # 提取文本内容
        try:
            text_content = analyzer.extract_text(temp_file_path)
        except Exception as e:
            raise HTTPException(
                status_code=400,
                detail=f"文件内容提取失败: {str(e)}"
            )
        
        if not text_content.strip():
            raise HTTPException(
                status_code=400,
                detail="文件中未提取到有效文本内容"
            )
        
        # 分析文件内容
        attendance_type = classify_attendance_type(text_content)
        company_name = extract_company_name(text_content)
        start_date, end_date = extract_date_range(text_content)
        validity_description, can_be_evidence = analyze_file_validity(text_content, attendance_type)
        
        # 构建响应
        response_data = {
            "文件类型": attendance_type,
            "主体公司名称": company_name,
            "起始日期": start_date,
            "结束日期": end_date,
            "文件内容": text_content[:1000] + "..." if len(text_content) > 1000 else text_content,  # 限制内容长度
            "文件有效性说明": validity_description,
            "是否可以作为证据": can_be_evidence
        }
        
        return JSONResponse(
            status_code=200,
            content=response_data
        )
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"服务器内部错误: {str(e)}"
        )
    
    finally:
        # 清理临时文件
        if temp_file and os.path.exists(temp_file_path):
            try:
                os.unlink(temp_file_path)
            except:
                pass


@app.get("/")
async def root():
    """API根路径"""
    return {
        "message": "考勤记录分析API",
        "version": "1.0.0",
        "endpoints": {
            "/analyze_attendance": "POST - 分析考勤记录文件"
        }
    }


@app.get("/health")
async def health_check():
    """健康检查接口"""
    return {"status": "healthy"}


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8003)